package com.example.videopractice.mediaex

import android.Manifest
import android.graphics.ImageFormat
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.Surface
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.example.base.base.BaseBindingActivity
import com.example.videopractice.databinding.ActivityCameraXvideoExBinding
import java.nio.ByteBuffer
import java.util.concurrent.Executors
import kotlin.math.min


class CameraXVideoExActivity : BaseBindingActivity<ActivityCameraXvideoExBinding>() {

    private val h264Encode = H264Encode()
    private var isStart = false
        set(value) {
            field = value
            binding.btnStart.isEnabled = !value
            binding.btnStop.isEnabled = value
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        requestMultiplePermissions.launch(
            arrayOf(
                Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.RECORD_AUDIO
            )
        )

        val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
        cameraProviderFuture.addListener(Runnable {
            val cameraX = cameraProviderFuture.get()

            val rotation = Surface.ROTATION_90
            val resolution = Size(640, 480)
            val preview = Preview.Builder()
                .setTargetRotation(rotation)
                .setTargetResolution(resolution)
                .build()
            preview.setSurfaceProvider(binding.preview.surfaceProvider)

            val imageAnalyzer = ImageAnalysis.Builder()
                .setTargetRotation(rotation)
                .setTargetResolution(resolution) //建议设置，设置不了需要旋转相机
                .setBackpressureStrategy(ImageAnalysis.STRATEGY_BLOCK_PRODUCER)
                .build()
            imageAnalyzer.setAnalyzer(Executors.newSingleThreadExecutor(), { image ->
                if (image.format == ImageFormat.YUV_420_888) {
                    /**
                     * 我们之前写好的h264工具类要求的编码格式是NV21 YYYYYYYYYYYYYYYYUVUVUVUV
                     * 所以我们要用YUV420来封装成NV21 ByteArray
                     **/
//                    val w = image.width
//                    val h = image.height
//                    val i420Size = w * h * 3 / 2
//                    val planes = image.planes
//                    val yRemaining = planes[0].buffer.remaining()
//                    val uRemaining = planes[1].buffer.remaining()
//                    val vRemaining = planes[2].buffer.remaining()
//                    val pixelStride = planes[2].pixelStride
//                    val rowOffset = planes[2].rowStride
//                    val nv21 = ByteArray(i420Size)
//
//                    val yRawSrcBytes = ByteArray(yRemaining)
//                    val uRawSrcBytes = ByteArray(uRemaining)
//                    val vRawSrcBytes = ByteArray(vRemaining)
//                    planes[0].buffer.get(yRawSrcBytes)
//                    planes[1].buffer.get(uRawSrcBytes)
//                    planes[2].buffer.get(vRawSrcBytes)
//                    if (pixelStride == w) {
//                        ALog.e("xiao","pixelStride == w")
//                        System.arraycopy(yRawSrcBytes, 0, nv21, 0, rowOffset * h)
//                        System.arraycopy(vRawSrcBytes, 0, nv21, rowOffset* h, rowOffset * h / 2 - 1)
//                    } else {
//                        val ySrcBytes = ByteArray(w * h)
//                        val uSrcBytes = ByteArray(w * h / 2 - 1)
//                        val vSrcBytes = ByteArray(w * h / 2 - 1)
//                        for (row in 0 until h) {
//                            System.arraycopy(yRawSrcBytes, rowOffset * row, ySrcBytes, w * row, w)
//                            if (row % 2 == 0) {
//                                if (row == h - 2) {
//                                    System.arraycopy(vRawSrcBytes, rowOffset * row / 2, vSrcBytes, w * row / 2, w - 1)
//                                } else {
//                                    System.arraycopy(vRawSrcBytes, rowOffset * row / 2, vSrcBytes, w * row / 2, w)
//                                }
//                            }
//                        }
//                        System.arraycopy(ySrcBytes, 0, nv21, 0, w * h)
//                     //   System.arraycopy(vSrcBytes, 0, nv21, w * h, w * h / 2 - 1)
//                    }
//                    h264Encode.putVideoData(nv21)

//                    val buffer = ByteArray(image.width * image.height * 3 / 2)
//                    readYuvDataToBuffer(image, buffer)
                    val buffer = yuv420ToNv21(image)
                    h264Encode.putVideoData(buffer)

//                    val size = image.width * image.height * 3 / 2
//                    val yuv420 = ByteBuffer.allocate(size)
//                    val planes = image.planes
//
//                    //y数据
//                    val yPixelStride = planes[0].pixelStride
//                    val yRowStride = planes[0].rowStride //可能末尾有填充
//                    val yBuffer = planes[0].buffer
//
//                    val row = ByteArray(image.width)
//                    //每行要排除的无效数据，但是需要注意：实际测试中 最后一行没有这个补位数据
//                    val skipRow = ByteArray(yRowStride - image.width)
//                    for (i in 0 until image.height) {
//                        yBuffer.get(row)
//                        yuv420.put(row)
//                        // 不是最后一行才有无效占位数据，最后一行因为后面跟着U 数据，没有无效占位数据，不需要丢弃
//                        if (i < image.height - 1) {
//                            yBuffer.get(skipRow)
//                        }
//                    }
//
//                    val uPixelStride = planes[1].pixelStride
//                    val uRowStride = planes[1].rowStride
//                    val uBuffer = planes[1].buffer
//
//                    val vPixelStride = planes[2].pixelStride
//                    val vRowStride = planes[2].rowStride
//                    val vBuffer = planes[2].buffer
//
//                    /**
//                     * U V 数据
//                     */
//                    for (i in 1..2) { //planes[1] | planes[2] uv数据处理
//                        val plane = planes[i]
//                        val pixelStride = plane.pixelStride //1 I420 2 交错packed UVUV
//                        // uv数据的rowStride可能是
//                        // 如：640的宽
//                        // 可能得到320， pixelStride 为1
//                        // 可能大于320同时小于640，有为了补位的无效数据  pixelStride 为1
//                        // 可能得到640 uv数据在一起，pixelStride为2
//                        // 可能大于640，有为了补位的无效数据 pixelStride为2
//                        val rowStride = plane.rowStride
//                        val buffer = plane.buffer
//                        val uvWidth = image.width / 2
//                        val uvHeight = image.height / 2
//                        for (j in 0 until uvHeight) {
//                            for (k in 0 until rowStride) {
//                                // 最后一行，是没有补位数据的
//                                if (j == uvHeight - 1) {
//                                    //只有自己(U/V)的数据
//                                    if (pixelStride == 1) {
//                                        // 结合外层if 则表示：
//                                        //  如果是最后一行，我们就不管结尾的占位数据了
//                                        if (k >= uvWidth) {
//                                            break
//                                        }
//                                    } else if (pixelStride == 2) {
//                                        //与同级if相同意思
//                                        // todo uv混合，
//                                        //  planes[2]:uvu
//                                        //  planes[3]:vuv
//                                        if (k >= image.width - 1) {
//                                            break
//                                        }
//                                    }
//                                }
//                                val b: Byte = buffer.get()
//                                if (pixelStride == 2) {
//                                    //打包格式 uv在一起,偶数位取出来是U数据： 0 2 4 6
//                                    if (k < image.width && k % 2 == 0) {
//                                        yuv420.put(b)
//                                    }
//                                } else if (pixelStride == 1) {
//                                    if (k < uvWidth) {
//                                        yuv420.put(b)
//                                    }
//                                }
//                            }
//                        }
//                    }
//
//
//
//                    val result = yuv420.array()
//                    h264Encode.putVideoData(result)
                }
                image.close()
            })

            val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

            try {
                cameraX.unbindAll()
                cameraX.bindToLifecycle(this, cameraSelector, imageAnalyzer, preview)
            } catch (e: Exception) {
                Log.e("xiao", "Use case binding failed", e)
            }

        }, ContextCompat.getMainExecutor(this))

        binding.btnStart.setOnClickListener {
            isStart = true
            h264Encode.startEncoder()
        }
        binding.btnStop.setOnClickListener {
            isStart = false
            h264Encode.stopEncoder()
        }
    }

    private fun readYuvDataToBuffer(image: ImageProxy, data: ByteArray): Boolean {
        if (image.format != ImageFormat.YUV_420_888) {
            throw IllegalArgumentException("only support ImageFormat.YUV_420_888 for mow")
        }
        val imageWidth = image.width
        val imageHeight = image.height
        val planes = image.planes
        var offset = 0
        for (plane in planes.indices) {
            val buffer = planes[plane].buffer ?: return false
            val rowStride = planes[plane].rowStride
            val pixelStride = planes[plane].pixelStride
            val planeWidth = if (plane == 0) imageWidth else imageWidth / 2
            val planeHeight = if (plane == 0) imageHeight else imageHeight / 2
            if (pixelStride == 1 && rowStride == planeWidth) {
                buffer.get(data, offset, planeWidth * planeHeight)
                offset += planeWidth * planeHeight
            } else {
                // Copy pixels one by one respecting pixelStride and rowStride
                val rowData = ByteArray(rowStride)
                var colOffset: Int
                for (row in 0 until planeHeight - 1) {
                    colOffset = 0
                    buffer.get(rowData, 0, rowStride)
                    for (col in 0 until planeWidth) {
                        data[offset++] = rowData[colOffset]
                        colOffset += pixelStride
                    }
                }
                // Last row is special in some devices:
                // may not contain the full |rowStride| bytes of data
                colOffset = 0
                buffer.get(rowData, 0, min(rowStride, buffer.remaining()))
                for (col in 0 until planeWidth) {
                    data[offset++] = rowData[colOffset]
                    colOffset += pixelStride
                }
            }
        }
        return true
    }

    fun yuv420ToNv21(image: ImageProxy): ByteArray {
        val planes = image.planes
        val yBuffer: ByteBuffer = planes[0].buffer
        val uBuffer: ByteBuffer = planes[1].buffer
        val vBuffer: ByteBuffer = planes[2].buffer
        val ySize: Int = yBuffer.remaining()
        val uSize: Int = uBuffer.remaining()
        val vSize: Int = vBuffer.remaining()
        val size = image.width * image.height
        val nv21 = ByteArray(ySize + vSize + 1)
//        ALog.e("xiao","ySize:${ySize}  yRowStride: ${planes[0].rowStride}  " +
//                "vRowStride: ${planes[2].rowStride}  totalSize:${nv21.size} " +
//                "vSize: ${vSize} height:${image.height} width: ${image.width}")
        yBuffer.get(nv21, 0, ySize)
        vBuffer.get(nv21, ySize, vSize)
        val u = ByteArray(uSize)
        uBuffer.get(u)

        //每隔开一位替换V，达到VU交替
        var pos = ySize + 1
        for (i in 0 until uSize) {
            if (i % 2 == 0) {
                nv21[pos] = u[i]
                pos += 2
            }
        }
        return nv21
    }
}